module csr_unit(
  input enable,
  input [7:0] op,
  input [63:0] src1,
  input [63:0] src2,
  input [63:0] imm,
  input [63:0] pc_i,

  input [4:0] dest,
  input [4:0] rs1,

  input unsupport_op,
  input ex_intr,
  input csr_mie_rdata,
//normal csr r/w port
  output [11:0] csr_addr,
  output csr_read,
  output csr_write,
  output csr_set,
  output csr_clear,
  output [63:0] csr_wdata,
  input [63:0] csr_rdata,
//specific csr r/w port
  output raise_intr,
  output raise_hard_intr,
  output op_mret,
//CSR mepc
  input [63:0] csr_mepc_rdata,
  output csr_mepc_write,
  output [63:0] csr_mepc_wdata,
//CSR mcause
  output csr_mcause_write,
  output [63:0] csr_mcause_wdata,
//CSR mtvec
  input [63:0] csr_mtvec_rdata,

  output [63:0] pc_o,
  output pc_valid_o,

  output [63:0] res,
  output valid_o
);
  wire op_csrrw,op_csrrs,op_csrrc,op_csrrwi,op_csrrsi,op_csrrci,op_mert,op_ecall;
  assign {op_csrrw,op_csrrs,op_csrrc,op_csrrwi,op_csrrsi,op_csrrci,op_mert,op_ecall} = op;
  
  assign op_mret = op_mert; /*an oolong event*/

  wire zicsr = {op_csrrw|op_csrrs|op_csrrc|op_csrrwi|op_csrrsi|op_csrrci};

  assign csr_addr = {12{zicsr}}&imm[11:0];

  assign csr_read = zicsr&!((op_csrrw|op_csrrwi)&dest==5'b0);
  assign csr_write = op_csrrw|op_csrrwi;
  assign csr_set = op_csrrs|(op_csrrsi&rs1!=5'b0);
  assign csr_clear = op_csrrc|(op_csrrci&rs1!=5'b0);

  assign csr_wdata = {64{op_csrrw|op_csrrs|op_csrrc}}&src1 | { 59'b0,{{5{op_csrrwi|op_csrrsi|op_csrrci}}&rs1}};

  assign res = csr_rdata;
  assign valid_o = csr_read;

  IRQ inst_IRQ
  (
    .enable         (enable),
    .ecall          (op_ecall),
    .mret           (op_mret),
    .pc_i           (pc_i),
    .unsupport_op   (unsupport_op),
    .ex_intr        (ex_intr&~(|op)),/* Make sure all csr operations will success affect intrrupt. */
    .mie_i          (csr_mie_rdata),
    .mtvec_i        (csr_mtvec_rdata),
    .mepc_i         (csr_mepc_rdata),
    .raise_intr     (raise_intr),
    .raise_hard_intr(raise_hard_intr),
    .mepc_o         (csr_mepc_wdata),
    .mepc_valid_o   (csr_mepc_write),
    .mcause_o       (csr_mcause_wdata),
    .mcause_valid_o (csr_mcause_write),
    .pc_o           (pc_o),
    .pc_valid_o     (pc_valid_o)
  );

endmodule
